// 之前已经学习了类的相关知识，也知道了类的继承。但是类之间的继承只能是单继承，如果我们想要多个类中的特性，啊么就需要考虑接口了
// 接口就是将一些共有的特性抽象出来，这一点和抽象类有点相似
// 定义接口使用interface，接口中定义的属性和方法不能有具体的实现。这一点不同于抽象类
interface Alarm {
  alert(): void; // 每辆车都有“跑”这个特性，我们就将其抽象出来
}
interface Light {
  lightOn(): void;
}

// implements 实现接口
class CarA implements Alarm,Light {
  lightOn(): void {}; // 实现类需要实现接口中所有的方法
  alert(): void {}; // 实现类需要实现接口中所有的方法
}

// 接口继承接口
// 接口可看成是一种特殊的类，两个接口间也是可以继承的，继承后的子接口拥有父接口中的属性和方法
interface LightAndAlarm extends Alarm {
  lightOn(): void;
}
class CarB implements LightAndAlarm {
  lightOn(): void {}; // 实现类需要实现接口中所有的方法
  alert(): void {}; // 实现类需要实现接口中所有的方法
}


// 接口继承类
// 在几乎所有的面向对象中，接口是不能继承自类的，因为接口和类在定义上有很大的不同，但是在TypeScript中是可以的。如果难以理解，那我们就一步一步来
// 1. 首先，当我们在声明一个类时，除了会创建一个类对象，还会创建一个同名的类型
class Person {
  name: string;
  // private sex: string = 'unkown';
  static age: number = 0
  constructor(name: string) {
    this.name = name;
  };
  getName(): string {
    return this.name;
  };
  static hello(): void{};
  // protected say(): void{};
}
// 此时的Person作为一个类型约束，其不包含构造函数、静态属性和方法
function getUserName(p: Person) {
  // new p(); // 编译错误，类型Person没有构造函数
  console.log('getUserName', p.getName());
}
getUserName(new Person('张三'))

// 2. 这个Person类型，我们可以使用一个PersonInstance接口代替
interface PersonInstance {
  name: string,
  getName(): any
}
function getUserName2(p: PersonInstance) {
  // new p(); // 编译错误，类型Person没有构造函数
  console.log('getUserName2', p.getName());
}
getUserName2(new Person('李四'))

// 3. 接口继承类，实际上等价于接口继承一个类型接口，该类型接口包含类的非构造函数、非静态属性和非静态方法，成员方法将只保留定义而没有具体的实现
interface Student extends Person {
  addr: string;
}
class Stu implements Student {
  addr: string = '';
  name: string = '';
  getName(): string {
    return 'getName'
  }
}
// 4. 接口继承类，类中不应该有私有属性和私有方法，也不能有受保护的属性和方法
// 5. 最后是不建议使用接口实现类
